var GlobalErrorListener = require('./GlobalErrorListener');
var globalOptions = require('./globalOptions');
var dataPath = require('./dataPath');
var o = [
    'created',
    'ready',
    'cacheAttached',
    'attached',
    'moved',
    'detached',
    'saved',
    'restored',
    'error',
    'listenerChanged',
    'performanceData'
];
var s = [
    String,
    Number,
    Boolean,
    Object,
    Array,
    null
];
var l = function () {
};
var parseMultiPaths = dataPath.parseMultiPaths;
var Behavior = function () {
};
Behavior.matchTypeWithValue = function (e, t) {
    if (e === String) {
        if ('string' != typeof t)
            return false;
    } else if (e === Number) {
        if (!Number.isFinite(t))
            return false;
    } else if (e === Boolean) {
        if ('boolean' != typeof t)
            return false;
    } else if (e === Object) {
        if (null === t || t.constructor !== Object)
            return false;
    } else if (e === Array) {
        if (null === t || t.constructor !== Array)
            return false;
    } else if (undefined === t)
        return false;
    return true;
};
var c = function (e) {
    var t = [];
    for (var i = 0; i < e.length; i++)
        t[i] = e[i];
    return t;
};
var h = function (e) {
    var t = {};
    for (var i in e)
        t[i] = e[i];
    return t;
};
var u = function (e, t) {
    for (var i in t)
        hasOwnProperty.call(e, i) ? 'object' != typeof e[i] || 'object' != typeof t[i] || null === t[i] || t[i] instanceof Array ? e[i] = t[i] : (e[i] instanceof Array ? e[i] = c(e[i]) : e[i] = h(e[i]), u(e[i], t[i])) : e[i] = t[i];
};
Behavior.create = function (e) {
    var t = new Behavior();
    t.is = e.is || '';
    t.using = e.using || {};
    t.generics = e.generics || {};
    t.placeholder = e.placeholder || {};
    t.template = e.template;
    t.externalClasses = e.externalClasses || [];
    t.data = 'object' == typeof e.data ? {} : null;
    t.properties = Object.create(null);
    t.methods = Object.create(null);
    t.listeners = Object.create(null);
    t.relations = Object.create(null);
    t.ancestors = [];
    t.initiator = e.initiator;
    t.lifetimes = Object.create(null);
    t.pageLifetimes = Object.create(null);
    t.observers = [];
    t.options = { publicProperties: !!(e.options && undefined !== e.options.publicProperties ? e.options.publicProperties : globalOptions.publicProperties) };
    t.definitionFilter = e.definitionFilter;
    t._unprepared = e;
    (e.options && undefined !== e.options.lazyRegistration ? e.options.lazyRegistration : globalOptions.lazyRegistration) || Behavior.prepare(t);
    e.is && (Behavior._list[e.is] = t);
    return t;
};
Behavior.prepare = function (e) {
    var t = e._unprepared;
    if (t) {
        e._unprepared = null;
        var i = e.ancestors;
        var a = '';
        var r = 0;
        for (r = 0; r < (t.behaviors || []).length; r++) {
            var c = t.behaviors[r];
            var h = c;
            for (a in ('string' == typeof h && (h = Behavior._list[c], GlobalErrorListener.hasGlobalWarningListeners() && (h instanceof Behavior || GlobalErrorListener.triggerWarning('behavior "' + c + '" is not found (when preparing "' + e.is + '").'))), h._unprepared && Behavior.prepare(h), 'object' == typeof h.data && (null === e.data ? e.data = h.data : u(e.data, h.data)), h.generics)) {
                var p = h.generics[a];
                'object' != typeof p && (p = {});
                e.generics[a] = { default: p.default };
            }
            for (a in h.properties)
                GlobalErrorListener.hasGlobalWarningListeners() && '_' === a.charAt(0) && undefined !== e.properties[a] && GlobalErrorListener.triggerWarning('property "' + a + '" from different behaviors is overriding (when preparing "' + e.is + '").'), e.properties[a] = h.properties[a];
            for (a in h.relations)
                e.relations[a] = h.relations[a];
            for (a in h.methods)
                GlobalErrorListener.hasGlobalWarningListeners() && '_' === a.charAt(0) && e.methods[a] && GlobalErrorListener.triggerWarning('method "' + a + '" from different behaviors is overriding (when preparing "' + e.is + '").'), e.methods[a] = h.methods[a];
            for (var g = 0; g < h.ancestors.length; g++)
                i.indexOf(h.ancestors[g]) < 0 && i.push(h.ancestors[g]);
        }
        for (a in ('object' == typeof t.data && (null === e.data ? e.data = t.data : u(e.data, t.data)), t.properties)) {
            GlobalErrorListener.hasGlobalWarningListeners() && '_' === a.charAt(0) && undefined !== e.properties[a] && GlobalErrorListener.triggerWarning('property "' + a + '" from different behaviors is overriding (when preparing "' + e.is + '").');
            var _ = t.properties[a];
            if (s.indexOf(_) >= 0) {
                _ = { type: _ };
            } else {
                s.indexOf(_.type) < 0 && _.optionalTypes && _.optionalTypes.length > 0 && (_.type = _.optionalTypes[0]);
            }
            GlobalErrorListener.hasGlobalWarningListeners() && s.indexOf(_.type) < 0 && GlobalErrorListener.triggerWarning('the type of property "' + a + '" is illegal (when preparing "' + e.is + '").');
            if (undefined === _.value) {
                if (_.type === String) {
                    _.value = '';
                } else {
                    if (_.type === Number) {
                        _.value = 0;
                    } else {
                        if (_.type === Boolean) {
                            _.value = false;
                        } else {
                            _.type === Array ? _.value = [] : _.value = null;
                        }
                    }
                }
            };
            e.properties[a] = {
                type: _.type,
                optionalTypes: _.optionalTypes,
                value: _.value,
                filter: _.filter,
                observer: _.observer,
                public: !!(undefined === _.public ? e.options.publicProperties : _.public),
                availability: _.availability,
                observeAssignments: !!_.observeAssignments,
                reflectIdPrefix: !!_.reflectIdPrefix
            };
        }
        if (t.lifetimes && 'object' == typeof t.lifetimes)
            for (a in t.lifetimes)
                e.lifetimes[a] = t.lifetimes[a];
        for (r = 0; r < o.length; r++)
            undefined === e.lifetimes[o[r]] && (e.lifetimes[o[r]] = t[o[r]]);
        for (a in t.listeners)
            e.listeners[a] = t.listeners[a];
        var f = t.observers;
        if (f instanceof Array)
            for (r = 0; r < f.length; r++) {
                var v = f[r];
                e.observers.push({
                    paths: parseMultiPaths(v.fields || '**'),
                    observer: v.observer
                });
            }
        else
            for (a in f)
                e.observers.push({
                    paths: parseMultiPaths(a),
                    observer: f[a]
                });
        if (t.pageLifetimes && 'object' == typeof t.pageLifetimes)
            for (a in t.pageLifetimes)
                e.pageLifetimes[a] = t.pageLifetimes[a];
        for (a in t.relations) {
            var w = t.relations[a];
            e.relations[a] = {
                target: w.target || a,
                type: w.type,
                linked: w.linked || l,
                linkChanged: w.linkChanged || l,
                unlinked: w.unlinked || l,
                linkFailed: w.linkFailed || l
            };
        }
        for (a in t.methods)
            'function' == typeof t.methods[a] && (GlobalErrorListener.hasGlobalWarningListeners() && '_' === a.charAt(0) && e.methods[a] && GlobalErrorListener.triggerWarning('method "' + a + '" from different behaviors is overriding (when preparing "' + e.is + '").'), e.methods[a] = t.methods[a]);
        i.push(e);
    }
};
Behavior._list = Object.create(null);
Behavior.prototype.hasBehavior = function (e) {
    this._unprepared && Behavior.prepare(this);
    for (var t = 0; t < this.ancestors.length; t++)
        if (e instanceof Behavior) {
            if (this.ancestors[t] === e)
                return true;
        } else if (this.ancestors[t] === Behavior._list[e])
            return true;
    return false;
};
Behavior.prototype._getAllListeners = function () {
    var e = {};
    var t = this.ancestors;
    for (var i = 0; i < t.length; i++) {
        var n = this.ancestors[i];
        for (var a in n.listeners)
            Object.prototype.hasOwnProperty.call(e, a) ? e[a].push(n.listeners[a]) : e[a] = [n.listeners[a]];
    }
    return e;
};
Behavior.prototype._addObserversToDataProxy = function (e) {
    var t = this.ancestors;
    for (var i = 0; i < t.length; i++)
        for (var n = this.ancestors[i].observers, a = 0; a < n.length; a++) {
            var r = n[a];
            e.addObserver(r.observer, r.paths, this);
        }
};
Behavior.prototype._getAllLifeTimeFuncs = function () {
    var e = {};
    var t = this.ancestors;
    o.forEach(function (t) {
        e[t] = GlobalErrorListener.create('Lifetime Method');
    });
    for (var i = 0; i < t.length; i++) {
        var a = t[i];
        for (var r in a.lifetimes)
            a.lifetimes[r] && (e[r] || (e[r] = GlobalErrorListener.create('Lifetime Method')), e[r].add(a.lifetimes[r]));
    }
    return e;
};
Behavior.prototype._getAllPageLifeTimeFuncs = function () {
    var e = {};
    var t = this.ancestors;
    for (var i = 0; i < t.length; i++) {
        var a = t[i];
        for (var r in a.pageLifetimes)
            a.pageLifetimes[r] && (e[r] || (e[r] = GlobalErrorListener.create('Page Lifetime Method')), e[r].add(a.pageLifetimes[r]));
    }
    return e;
};
Behavior.callDefinitionFilter = function (e) {
    var t = e.definitionFilter;
    var i = [];
    e.behaviors instanceof Array && e.behaviors.forEach(function (t) {
        if ((t = 'string' == typeof t ? Behavior._list[t] : t) && t.definitionFilter) {
            i.push(t.definitionFilter);
            t.definitionFilter.call(null, e);
        };
    });
    return 'function' == typeof t ? function (e) {
        t(e, i);
    } : null;
};
module.exports = Behavior;
